/*
 * Copyright 2002-2019 Intel Corporation.
 * 
 * This software is provided to you as Sample Source Code as defined in the accompanying
 * End User License Agreement for the Intel(R) Software Development Products ("Agreement")
 * section 1.L.
 * 
 * This software and the related documents are provided as is, with no express or implied
 * warranties, other than those that are expressly stated in the License.
 */


/* ===================================================================== */
/*! @file
 * 
 * This file shows how functions with floating point arguments can be wrapped.
 * FP wrappers are not supported in Pin yet.
*/

/* ===================================================================== */
#include "pin.H"
#include <iostream>
#include <string.h>
#include <stdio.h>


/* ===================================================================== */

struct DOUBLE_STRUCT
{
    int _d0;
    int _d1;
};

/*
 * A wrapper for
 *  int func(int, float, int)
 */
int fp_func3_wrapper( CONTEXT * ctxt, AFUNPTR fp_func3_ptr, int i1, float f2, int i3 )
{

    int res;
#ifdef TARGET_IA32
    printf("fp_func3_wrapper parameters: %d, %f, %d\n", i1, f2, i3);
    fflush(stdout);

    /*
     * parameter of type "float" is on stack and located in 4 bytes like integer
    */
    int f2_int = *(int *)(&f2);
    PIN_CallApplicationFunction( ctxt, PIN_ThreadId(),
                                 CALLINGSTD_DEFAULT, fp_func3_ptr, NULL,
                                 PIN_PARG(int), &res,
                                 PIN_PARG(int), i1,
                                 PIN_PARG(int), f2_int,
                                 PIN_PARG(int), i3,
                                 PIN_PARG_END() );
#else

    /*
    * all fp parameters are in xmm registers
    * take them from the context
    */
    FPSTATE fpState;
    PIN_GetContextFPState(ctxt, &fpState);
    memcpy(&f2, &fpState.fxsave_legacy._xmms[0], sizeof(f2));
    printf("fp_func3_wrapper parameters: %d, %f, %d\n", i1, f2, i3);
    fflush(stdout);

    // all fp args are in xmm regs in context - do not pass any args to the app function
    PIN_CallApplicationFunction( ctxt, PIN_ThreadId(),
                                 CALLINGSTD_DEFAULT, fp_func3_ptr, NULL,
                                 PIN_PARG(int), &res,
                                 PIN_PARG(int), i1,
                                 PIN_PARG(int), i3,
                                 PIN_PARG_END() );
#endif

    return res;
}


/*
 * A wrapper for
 *  float func(double, float, int, double)
 */
float fp_func4_wrapper( CONTEXT * ctxt, AFUNPTR fp_ptr, double d1, float f2, int i3,  double d4)
{
    float resf;

#ifdef TARGET_IA32
    printf("fp_func4_wrapper parameters: %e, %f, %d, %e\n", d1, f2, i3, d4);
    fflush(stdout);

    /*
     * parameter of type "double" is on stack and located in 8 bytes
    */
    DOUBLE_STRUCT *d_param1 = (DOUBLE_STRUCT *)&d1;
    DOUBLE_STRUCT *d_param4 = (DOUBLE_STRUCT *)&d4;


    /*
    * parameter of type "float" is on stack and located in 4 bytes like integer
    */
    int f2_int = *(int *)(&f2);
    PIN_CallApplicationFunction( ctxt, PIN_ThreadId(),
                                 CALLINGSTD_DEFAULT, fp_ptr, NULL,
                                 PIN_PARG(float), &resf,
                                 PIN_PARG(int), d_param1->_d0, // lower part of d1
                                 PIN_PARG(int), d_param1->_d1, // upper part of d1
                                 PIN_PARG(int), f2_int,
                                 PIN_PARG(int), i3,
                                 PIN_PARG(int), d_param4->_d0, // lower part of d4
                                 PIN_PARG(int), d_param4->_d1, // upper part of d4
                                 PIN_PARG_END() );

#else
    FPSTATE fpState;

    /*
    * all fp parameters are in xmm registers
    * take them from the context
    */
    PIN_GetContextFPState(ctxt, &fpState);
    memcpy(&d1, &fpState.fxsave_legacy._xmms[0], sizeof(d1));
    memcpy(&f2, &fpState.fxsave_legacy._xmms[1], sizeof(f2));
    memcpy(&d4, &fpState.fxsave_legacy._xmms[2], sizeof(d4));


    printf("fp_func4_wrapper parameters: %e, %f, %d, %e\n", d1, f2, i3, d4);
    fflush(stdout);

    // all fp args are in xmm regs in context - only pass the int args to the app function
    PIN_CallApplicationFunction( ctxt, PIN_ThreadId(),
                                 CALLINGSTD_DEFAULT, fp_ptr, NULL,
                                 PIN_PARG(float), &resf,
                                 PIN_PARG(int), i3,
                                 PIN_PARG_END() );

#endif
    printf("fp_func4_wrapper result: %f\n", resf);
    fflush(stdout);

    return resf;
}


double fp_func1_wrapper( CONTEXT * ctxt, AFUNPTR fp_ptr, double d1)
{
    double resd;

#ifdef TARGET_IA32
    printf("fp_func1_wrapper parameters: %e\n", d1);
    fflush(stdout);

    /*
     * parameter of type "double" is on stack and located in 8 bytes
    */
    DOUBLE_STRUCT *d_param1 = (DOUBLE_STRUCT *)&d1;


    PIN_CallApplicationFunction( ctxt, PIN_ThreadId(),
                                 CALLINGSTD_DEFAULT, fp_ptr, NULL,
                                 PIN_PARG(double), &resd,
                                 PIN_PARG(int), d_param1->_d0,
                                 PIN_PARG(int), d_param1->_d1,
                                 PIN_PARG_END() );


#else
    FPSTATE fpState;

    /*
    * all fp parameters are in xmm registers
    * take them from the context
    */

    PIN_GetContextFPState(ctxt, &fpState);
    memcpy(&d1, &fpState.fxsave_legacy._xmms[0], sizeof(d1));
    printf("fp_func1_wrapper parameters: %e\n", d1);
    fflush(stdout);
    // all fp args are in xmm regs in context - do not pass any args to the app function
    PIN_CallApplicationFunction( ctxt, PIN_ThreadId(),
                                 CALLINGSTD_DEFAULT, fp_ptr, NULL,
                                 PIN_PARG(double), &resd,
                                 PIN_PARG_END() );


#endif

    printf("fp_func1_wrapper result: %e\n", resd);
    fflush(stdout);

    return resd;
}

/* ===================================================================== */
VOID ImageLoad(IMG img, VOID *v)
{
    /*
    * Instrument function
    *  int func3(double, float, int, double)
    */
    RTN rtn = RTN_FindByName(img, "fp_func3");
    if (RTN_Valid(rtn))
    {
        //printf("Replace fp_func3\n");
#ifdef TARGET_IA32
        PROTO proto_func3 = PROTO_Allocate( PIN_PARG(int), CALLINGSTD_DEFAULT,
                                            "fp_func3", PIN_PARG(int), PIN_PARG(int),
                                            PIN_PARG(int), PIN_PARG_END() );

        RTN_ReplaceSignature(
            rtn, AFUNPTR(fp_func3_wrapper),
            IARG_PROTOTYPE, proto_func3,
            IARG_CONTEXT,
            IARG_ORIG_FUNCPTR,
            IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
            IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
            IARG_FUNCARG_ENTRYPOINT_VALUE, 2,
            IARG_END);
#else // TARGET_IA32E
        PROTO proto_func3 = PROTO_Allocate( PIN_PARG(int), CALLINGSTD_DEFAULT,
                                            "fp_func3", PIN_PARG(int), PIN_PARG(int),
                                            PIN_PARG_END() );

        RTN_ReplaceSignature(
                rtn, AFUNPTR(fp_func3_wrapper),
        IARG_PROTOTYPE, proto_func3,
        IARG_CONTEXT,
        IARG_ORIG_FUNCPTR,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
        IARG_END);

#endif

        PROTO_Free( proto_func3 );
    }

    rtn = RTN_FindByName(img, "fp_func4");
    if (RTN_Valid(rtn))
    {
        //printf("Replace fp_func4\n");
        //
        // float fp_func4(double d1, float f2, int i3, double d4)
        //
#ifdef TARGET_IA32
        PROTO proto_func4 = PROTO_Allocate( PIN_PARG(float), CALLINGSTD_DEFAULT,
                                            "fp_func4",
                                            PIN_PARG(int), // double p1
                                            PIN_PARG(int), // double p2
                                            PIN_PARG(int), // float
                                            PIN_PARG(int), // int
                                            PIN_PARG(int), // double p1
                                            PIN_PARG(int), // double p2

                                            PIN_PARG_END() );

        RTN_ReplaceSignature(
                rtn, AFUNPTR(fp_func4_wrapper),
        IARG_PROTOTYPE, proto_func4,
        IARG_CONTEXT,
        IARG_ORIG_FUNCPTR,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 2,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 3,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 4,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 5,
        IARG_END);
#else
        // only i3 is in register, other parameters in xmm
        PROTO proto_func4 = PROTO_Allocate( PIN_PARG(float), CALLINGSTD_DEFAULT,
                                            "fp_func4",
                                            PIN_PARG(int),
                                            PIN_PARG_END() );

        RTN_ReplaceSignature(
                rtn, AFUNPTR(fp_func4_wrapper),
        IARG_PROTOTYPE, proto_func4,
        IARG_CONTEXT,
        IARG_ORIG_FUNCPTR,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
        IARG_END);

#endif

        PROTO_Free( proto_func4 );
    }

    rtn = RTN_FindByName(img, "fp_func1");
    if (RTN_Valid(rtn))
    {
        //printf("Replace fp_func1\n");
        //
        // double fp_func1(double d1)
        //
#ifdef TARGET_IA32

        // d1 on stack, 8 bytes
        // passed as 2 integers
        PROTO proto_func1 = PROTO_Allocate( PIN_PARG(double), CALLINGSTD_DEFAULT,
                                            "fp_func1",
                                            PIN_PARG(int), // d1 lower
                                            PIN_PARG(int), // d1 upper
                                            PIN_PARG_END() );

        RTN_ReplaceSignature(
                rtn, AFUNPTR(fp_func1_wrapper),
        IARG_PROTOTYPE, proto_func1,
        IARG_CONTEXT,
        IARG_ORIG_FUNCPTR,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 1,
        IARG_END);
#else
        // all parameters in xmm, will be taken from context; nothing to be passed
        PROTO proto_func1 = PROTO_Allocate( PIN_PARG(double), CALLINGSTD_DEFAULT,
                                            "fp_func1",
                                            PIN_PARG_END() );

        RTN_ReplaceSignature(
                rtn, AFUNPTR(fp_func1_wrapper),
        IARG_PROTOTYPE, proto_func1,
        IARG_CONTEXT,
        IARG_ORIG_FUNCPTR,
        IARG_END);

#endif

        PROTO_Free( proto_func1 );
    }
}

/* ===================================================================== */
int main(INT32 argc, CHAR *argv[])
{
    PIN_InitSymbols();

    PIN_Init(argc, argv);

    IMG_AddInstrumentFunction(ImageLoad, 0);

    PIN_StartProgram();

    return 0;
}

/* ===================================================================== */
/* eof */
/* ===================================================================== */

